#include "ASList.h"
#include "ASListItem.h"

static as_listitem* as_ctrl_list_get_item_from_idx(as_control* self, ASINT idx)
{
    as_list* list_p = (as_list*)self;
    as_listitem* tmp_p;
    ASINT i = 0;

    if(!list_p)
        return NULL;

    if(idx < 0)
        idx = 0;

    if(idx >= list_p->item_counts)
        idx = list_p->item_counts - 1;

    tmp_p = list_p->head;

    while (tmp_p)
    {
        if(i == idx)
            break;

        i++;
        tmp_p = tmp_p->get_next_item((as_control*)tmp_p);
    }

    return tmp_p;
}

static ASINT as_ctrl_list_get_idx_from_item(as_control* self, as_listitem* item)
{
    as_list* list_p = (as_list*)self;
    as_listitem* tmp_p;
    ASINT i = 0;

    if(!list_p || !item)
        return -1;

    tmp_p = list_p->head;

    while (tmp_p)
    {
        if(tmp_p == item)
            break;

        i++;
        tmp_p = tmp_p->get_next_item((as_control*)tmp_p);
    }

    return i;
}

static ASINT as_ctrl_list_item_if_need_hide(as_list* self, as_listitem* item_p)
{
    if(!self || !item_p)
        return TRUE;

    if(item_p->base.area.y + item_p->base.area.h <= self->base.area.y
       || item_p->base.area.y >= self->base.area.y + self->base.area.h)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

static as_listitem* as_ctrl_list_add_item(as_control* self, as_rect area)
{
    as_list* list_p = (as_list*)self;
    as_listitem* item_p;

    if(!list_p)
    {
        return NULL;
    }

    item_p = as_ctrl_listitem_create(area, &list_p->base.area);

    if(!item_p)
        return NULL;

    list_p->items_total_height += area.h;
    list_p->item_counts++;

    //the first item
    if(list_p->head == NULL)
    {
        list_p->tail = list_p->head = item_p;
        item_p->set_next_item((as_control*)item_p, NULL);
        item_p->set_prev_item((as_control*)item_p, NULL);
        //list_p->set_selected_idx((as_control*)list_p, 0);
    }
    //the second
    else if(list_p->head && list_p->head == list_p->tail)
    {
        list_p->head->set_next_item((as_control*)list_p->head, item_p);
        item_p->set_prev_item((as_control*)item_p, list_p->head);
        item_p->set_next_item((as_control*)item_p, NULL);
        list_p->tail = item_p;
    }
    else
    {
        item_p->set_prev_item((as_control*)item_p, list_p->tail);
        item_p->set_next_item((as_control*)item_p, NULL);
        list_p->tail->set_next_item((as_control*)list_p->tail, item_p);
        list_p->tail = item_p;
    }

    //set hidden
    if(as_ctrl_list_item_if_need_hide(list_p, item_p) == TRUE)
    {
        item_p->base.set_hidden((as_control*)item_p, TRUE);
    }

    return item_p;
}

//with bug
static as_ret as_ctrl_list_delete_item(as_control* self, as_listitem* node_p)
{
    as_list* list_p = (as_list*)self;
    as_listitem *tmp_prev_p, *tmp_next_p;
    ASINT h, node_idx;
    ASINT is_head = FALSE, is_tail = FALSE, is_middle = FALSE;

    if(!self || !node_p || !list_p->head)
        return AS_INVALID_PARAM;

    //only one item
    if(list_p->head == node_p && node_p->get_next_item((as_control*)node_p) == NULL)
    {
        list_p->clean_items((as_control*)list_p);
        return AS_OK;
    }

    node_idx = list_p->get_idx_from_item((as_control*)list_p, node_p);
    h = node_p->base.area.h;
    tmp_prev_p = node_p->get_prev_item((as_control*)node_p);
    tmp_next_p = node_p->get_next_item((as_control*)node_p);

    if(tmp_prev_p)
    {
        is_head = FALSE;
        tmp_prev_p->set_next_item((as_control*)tmp_prev_p, tmp_next_p);
    }
    else
    {
        is_head = TRUE;
    }

    if(tmp_next_p)
    {
        is_tail = FALSE;
        tmp_next_p->set_prev_item((as_control*)tmp_next_p, tmp_prev_p);
    }
    else
    {
        is_tail = TRUE;
    }

    if(is_head == FALSE && is_tail == FALSE)
    {
        is_middle = TRUE;
    }

    if(is_head)
    {
        as_listitem* item_p;

        if(list_p->select_item == node_p)
        {
            list_p->select_item = tmp_next_p;
            list_p->select_item->set_focus((as_control*)list_p->select_item, TRUE);
        }

        list_p->head = tmp_next_p;
        item_p = list_p->head;

        while (item_p)
        {
            item_p->base.move((as_control*)item_p, 0, -h);
            item_p = item_p->get_next_item((as_control*)item_p);
        }
    }
    else if(is_tail)
    {
        if(list_p->select_item == node_p)
        {
            list_p->select_item = tmp_prev_p;

            if(list_p->head->base.area.y < list_p->base.area.y)
                list_p->base.move((as_control*)list_p, 0, h);

            list_p->select_item->set_focus((as_control*)list_p->select_item, TRUE);
        }

        list_p->tail = tmp_prev_p;
        list_p->select_idx--;
    }
    else if(is_middle)
    {
        list_p->select_item = tmp_next_p;
    }

    as_ctrl_listitem_destroy(node_p);
    node_p = NULL;
    list_p->item_counts--;
    list_p->items_total_height -= h;
    return AS_OK;
}

static ASINT as_ctrl_list_get_node_counts(as_control* self)
{
    as_list* list_p = (as_list*)self;

    if(!list_p)
        return -1;

    return list_p->item_counts;
}

static as_ret as_ctrl_list_set_bg_color(as_control* self, ASUINT color)
{
    as_list* list_p = (as_list*)self;

    if(!list_p)
    {
        return AS_INVALID_PARAM;
    }

    list_p->bg_color = color;
    return AS_OK;
}

static as_ret as_ctrl_list_set_bg_image(as_control* self, ASWSTR path, as_image_src_type type)
{
    as_ret ret = AS_OK;
    as_list* list_p = (as_list*)self;
    ASUINT param[3];

    if(!list_p || !path || as_wstrlen(path) <= 0)
    {
        ret = AS_INVALID_PARAM;
        return ret;
    }

    if(list_p->bg_image)
    {
        as_ctrl_destroy((as_control*)list_p->bg_image);
    }

    param[0] = (ASUINT)type;
    param[1] = (ASUINT)path;
    param[2] = (ASUINT)FALSE;
    list_p->bg_image = (as_image*)as_ctrl_create(AS_CTRL_IMAGE, list_p->base.area, 3, (void**)param);

    if(list_p->bg_image == NULL)
    {
        ret = AS_MEMORY_NOT_ENOUGH;
        return ret;
    }

    return ret;
}

static void as_ctrl_list_calc_items_section_height(as_list* self, ASINT* height_unselect_1,
        ASINT* height_select, ASINT* height_unselect_2)
{
    as_listitem* item_p;

    if(!self || !height_unselect_1 || !height_select || !height_unselect_2)
        return;

    item_p = self->head;

    if(self->item_counts == 0)
    {
        *height_unselect_1 = *height_unselect_2 = 0;
        *height_select = self->base.area.h;
        return;
    }

    * height_unselect_1 = * height_unselect_2 = *height_select = 0;

    while(item_p && item_p->base.get_hidden((as_control*)item_p) == TRUE)
    {
        * height_unselect_1 += item_p->base.area.h;
        item_p = item_p->get_next_item((as_control*)item_p);
    }

    while(item_p && item_p->base.get_hidden((as_control*)item_p) == FALSE)
    {
        * height_select += item_p->base.area.h;
        item_p = item_p->get_next_item((as_control*)item_p);
    }

    while(item_p && item_p->base.get_hidden((as_control*)item_p) == TRUE)
    {
        * height_unselect_2 += item_p->base.area.h;
        item_p = item_p->get_next_item((as_control*)item_p);
    }
}

static void as_ctrl_list_calc_scrollbar(as_list* self, as_rect* dst_area_p)
{
    as_rect area;
    ASINT h_us_1, h_us_2, h_s;

    if(!self || !dst_area_p)
        return;

    h_us_1 = h_us_2 = h_s = 0;
    as_ctrl_list_calc_items_section_height(self, &h_us_1, &h_s, &h_us_2);
    self->items_total_height = h_us_1 + h_s + h_us_2;//adjust the total height
    area.w = (ASINT16)self->scrollbar_width;
    area.x = self->base.area.x + self->base.area.w - 1 - area.w;

    if(self->item_counts == 0)
    {
        area.y = self->base.area.y;
        area.h = self->base.area.h;
    }
    else
    {
        ASINT h_adjust;
        area.h = (ASINT16)(h_s * self->base.area.h / self->items_total_height);
        area.y = (ASINT16)(self->base.area.y + (h_us_1 * self->base.area.h / self->items_total_height));
        h_adjust = area.h - self->scrollbar_min_height;

        if(h_adjust < 0)
        {
            area.h += (ASINT16) - h_adjust;

            if(area.y - self->base.area.y > -h_adjust)
                area.y -= (ASINT16) - h_adjust;
        }
    }

    memcpy(dst_area_p, &area, sizeof(as_rect));
}

static void as_ctrl_list_paint_scrollbar(as_list* self)
{
    as_rect rect;
    ASINT layer;
    ASUINT8* paint_buf;

    if(!self)
        return;

    as_ctrl_list_calc_scrollbar(self, &rect);
    layer = self->base.show_hdlr;
    paint_buf = as_get_layer_buffer_by_hdlr(layer);

    if(self->scrollbar_tail_img && self->scrollbar_tile_img)
    {
        if(rect.h <= self->scrollbar_min_height)
        {
            self->scrollbar_tail_img->base.move((as_control*)self->scrollbar_tail_img, rect.x, rect.y);
            self->scrollbar_tail_img->base.paint_hdlr((as_control*)self->scrollbar_tail_img);
        }
        else
        {
            as_rect area;
            layer = self->base.show_hdlr;
            memcpy(&area, &rect, sizeof(as_rect));
            area.h = (ASINT16)self->scrollbar_min_height >> 1;
            as_set_layer_clip_ex(layer, &area);
            self->scrollbar_tail_img->base.change_area_hdlr((as_control*)self->scrollbar_tail_img, area);
            self->scrollbar_tail_img->base.paint_hdlr((as_control*)self->scrollbar_tail_img);
            as_reset_layer_clip(layer);
            area.y = rect.y + (ASINT16)(self->scrollbar_min_height >> 1);
            area.h = rect.h - (ASINT16)self->scrollbar_min_height;
            self->scrollbar_tile_img->base.change_area_hdlr((as_control*)self->scrollbar_tile_img, area);
            as_set_layer_clip_ex(layer, &area);
            self->scrollbar_tile_img->base.paint_hdlr((as_control*)self->scrollbar_tile_img);
            as_reset_layer_clip(layer);
            area.y += area.h;
            area.h = (ASINT16)self->scrollbar_min_height >> 1;
            as_set_layer_clip_ex(layer, &area);
            area.y -= (ASINT16)(self->scrollbar_min_height >> 1);
            area.h += area.h;
            self->scrollbar_tail_img->base.change_area_hdlr((as_control*)self->scrollbar_tail_img, area);
            self->scrollbar_tail_img->base.paint_hdlr((as_control*)self->scrollbar_tail_img);
        }
    }
    else
    {
        as_fill_rect(&rect, self->scrollbar_color, self->scrollbar_color);
    }
}

static void as_ctrl_list_paint_bg(as_list* self)
{
    as_rect* area_p;
    ASUINT8* paint_buf;

    if(!self)
        return;

    area_p = &self->base.area;

    if(self->bg_image)
    {
        ((as_control*)self->bg_image)->paint_hdlr((as_control*)self->bg_image);
    }
    else
    {
        paint_buf = as_get_layer_buffer_by_hdlr(self->base.show_hdlr);
        as_fill_rect(area_p, self->bg_color, self->bg_color);
    }
}


static void as_ctrl_list_paint_items(as_list* self)
{
    as_list* list_p = (as_list*)self;
    as_listitem* tmp_p;
    as_rect* item_area_p;
    ASINT layer;
    ASUINT8* paint_buf;

    if(!list_p)
        return;

    tmp_p = list_p->head;
    layer = list_p->base.show_hdlr;
    paint_buf = as_get_layer_buffer_by_hdlr(layer);

    if(!tmp_p)
    {
        ASWSTR text = list_p->empty_text;
        ASINT w = as_get_wstr_width_sf(text);
        ASINT h = as_get_wstr_height_sf(text);
        ASINT x, y;
        item_area_p = &list_p->base.area;

        if(layer < 0 || as_set_layer_clip_ex(layer, item_area_p) == AS_IGNORE)
            return;

        x = item_area_p->x + (item_area_p->w - w) / 2;
        y = item_area_p->y + (item_area_p->h - h) / 2;
        as_draw_text_sf(text, x, y, list_p->empty_text_color);
        as_reset_layer_clip(layer);
        return;
    }

    item_area_p = &list_p->base.area;

    if(layer < 0 || as_set_layer_clip_ex(layer, item_area_p) == AS_IGNORE)
        return;

    //todo: clean the area with tansparent color
    as_ctrl_list_paint_bg(self);

    while(tmp_p)
    {
        if(tmp_p->base.get_hidden((as_control*)tmp_p) == FALSE)
        {
            tmp_p->base.paint_hdlr((as_control*)tmp_p);
#ifdef AS_RUN_ON_SKY
            as_set_layer_clip_ex(layer, item_area_p);
#endif

            if(list_p->grid_color_cnts != 0)
            {
                //if(list_p->select_item != tmp_p)
                //{
                //     item_area_p = &list_p->base.area;
                //     if(as_set_layer_clip_ex(layer, item_area_p) == AS_IGNORE)
                //         break;
                //     item_area_p = &tmp_p->base.area;
                //    if(as_set_layer_clip_ex(layer, item_area_p) == AS_IGNORE)
                //     {
                //        tmp_p = tmp_p->get_next_item((as_control*)tmp_p);
                //         continue;
                //    }
                //     if(as_set_layer_clip_ex(layer, &tmp_p->father_area) == AS_IGNORE)
                //        break;
                //    //paint top line and bottom line
                //    as_draw_h_line(item_area_p->x, item_area_p->x + item_area_p->w - 1,
                //                   item_area_p->y, list_p->grid_colors[0]);
                //    as_draw_h_line(item_area_p->x, item_area_p->x + item_area_p->w - 1,
                //                   item_area_p->y + 1, list_p->grid_colors[1]);
                //    if(list_p->select_item != tmp_p)
                //    {
                //        as_draw_h_line(item_area_p->x, item_area_p->x + item_area_p->w - 1,
                //                       item_area_p->y + item_area_p->h, list_p->grid_colors[0]);
                //        as_draw_h_line(item_area_p->x, item_area_p->x + item_area_p->w - 1,
                //                       item_area_p->y + item_area_p->h + 1, list_p->grid_colors[1]);
                //    }
                //    as_reset_clip();
                //}
            }
        }

        tmp_p = tmp_p->get_next_item((as_control*)tmp_p);
    }

    if(list_p->if_show_scrollbar)
    {
        as_ctrl_list_paint_scrollbar(self);
    }

    as_reset_layer_clip(layer);
}

static void as_ctrl_list_paint(as_control* self)
{
    as_list* list_p = (as_list*)self;

    if(!list_p || self->if_hidden)
        return;

    //draw background
    as_ctrl_list_paint_bg(list_p);
    //draw items
    as_ctrl_list_paint_items(list_p);
}

//internal
static void as_ctrl_list_change_selected_item(as_list* list_p, as_listitem* item_p, ASINT idx)
{
    if(!list_p)
        return;

    list_p->last_select_item = list_p->select_item;
    list_p->last_select_idx = list_p->select_idx;

    if(list_p->last_select_item)
    {
        list_p->last_select_item->set_focus((as_control*)list_p->last_select_item, FALSE);
    }

    list_p->select_item = item_p;
    list_p->select_idx = idx;

    if(list_p->select_item)
    {
        list_p->select_item->set_focus((as_control*)list_p->select_item, TRUE);
    }
}

static as_ret as_ctrl_list_set_selected_idx(as_control* self, ASINT idx)
{
    as_list* list_p = (as_list*)self;
    as_listitem* tmp_p;

    if(!list_p || idx == list_p->select_idx)
        return AS_INVALID_PARAM;

    if(idx < 0)
    {
        //warn
        idx = 0;

        if(!list_p->head)
            return AS_INTERNAL_ERROR;

        as_ctrl_list_change_selected_item(list_p, list_p->head, idx);
    }
    else if(idx >= list_p->item_counts)
    {
        idx = list_p->item_counts - 1;

        if(!list_p->tail)
            return AS_INTERNAL_ERROR;

        as_ctrl_list_change_selected_item(list_p, list_p->tail, idx);
    }
    else
    {
        tmp_p = list_p->get_item_from_idx((as_control*)list_p, idx);
        as_ctrl_list_change_selected_item(list_p, tmp_p, idx);
    }

    return AS_OK;
}

static as_ret as_ctrl_list_select_prev_item(as_control* self)
{
    as_list* list_p = (as_list*)self;
    ASINT idx = 0;
    as_listitem* li_p;

    if(!list_p)
        return AS_INVALID_PARAM;

    idx = list_p->select_idx - 1;

    if(idx < 0)
        idx = list_p->item_counts - 1;

    li_p = list_p->get_item_from_idx((as_control*)list_p, idx);

    while(!li_p->could_selected)
    {
        as_ctrl_list_set_selected_idx(self, idx);
        list_p->adjust_select_item_position((as_control*)list_p);
        idx--;

        if(idx < 0)
            idx = list_p->item_counts - 1;

        li_p = list_p->get_item_from_idx((as_control*)list_p, idx);
    }

    return as_ctrl_list_set_selected_idx(self, idx);
}

static as_ret as_ctrl_list_select_next_item(as_control* self)
{
    as_list* list_p = (as_list*)self;
    ASINT idx = 0;
    as_listitem* li_p;

    if(!list_p)
        return AS_INVALID_PARAM;

    idx = list_p->select_idx + 1;

    if(idx >= list_p->item_counts)
        idx = 0;

    li_p = list_p->get_item_from_idx((as_control*)list_p, idx);

    while(!li_p->could_selected)
    {
        as_ctrl_list_set_selected_idx(self, idx);
        list_p->adjust_select_item_position((as_control*)list_p);
        idx++;

        if(idx >= list_p->item_counts)
            idx = 0;

        li_p = list_p->get_item_from_idx((as_control*)list_p, idx);
    }

    return as_ctrl_list_set_selected_idx(self, idx);
}

static void as_ctrl_list_adjust_selected_item_position(as_control* self)
{
    ASINT y_off;
    as_list* list_p = (as_list*)self;

    if(!list_p || !list_p->select_item)
        return;

    y_off = list_p->select_item->base.area.y - list_p->base.area.y;

    if(y_off < 0)
    {
        list_p->base.move((as_control*)list_p, 0, -y_off);
    }

    y_off = (list_p->select_item->base.area.y + list_p->select_item->base.area.h)
            - (list_p->base.area.y + list_p->base.area.h);

    if(y_off > 0)
    {
        list_p->base.move((as_control*)list_p, 0, -y_off);
    }
}


static as_listitem* as_ctrl_list_get_item_from_point(as_list* list_p, ASINT x, ASINT y)
{
    as_listitem* item_p;

    if(!list_p)
        return NULL;

    item_p = list_p->head;

    while (item_p)
    {
        if(AS_POINT_IN_RECT(x, y, item_p->base.area))
            break;

        item_p = item_p->get_next_item((as_control*)item_p);
    }

    return item_p;
}

//static ASINT as_ctrl_list_get_items_total_height(as_list* list_p)
//{
//    as_listitem* item_p;
//    ASINT height = 0;
//    if(!list_p)
//        return -1;
//
//    item_p = list_p->head;
//    while(item_p)
//    {
//        height += item_p->base.area.h;
//        item_p = item_p->get_next_item((as_control*)item_p);
//    }
//    return height;
//}

static ASINT as_ctrl_list_if_items_need_scroll_up(as_list* list_p)
{
    if(!list_p)
        return FALSE;

    if(list_p->items_total_height > list_p->base.area.h)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

//according items current position,set each item hidden or not.
//static as_listitem* as_ctrl_list_get_show_start_item(as_list* list_p)
//{
//    as_listitem* item_p;
//    ASINT i,total;
//
//    if(!list_p)
//        return NULL;
//
//    total = list_p->item_counts;
//    i = total >> 1;
//    item_p = list_p->get_item_from_idx((as_control*)list_p,i);
//}

static void as_ctrl_list_update_items_hidden_state(as_list* list_p)
{
    as_listitem* item_p = NULL;
    as_listitem* item_show_start_p = NULL;
    as_listitem* item_show_end_p = NULL;

    if(!list_p)
        return;

    item_p = list_p->head;

    while(item_p)
    {
        if(as_ctrl_list_item_if_need_hide(list_p, item_p) == FALSE)
        {
            item_show_start_p = item_p;
            break;
        }
        else
        {
            item_p->base.set_hidden((as_control*)item_p, TRUE);
        }

        item_p = item_p->get_next_item((as_control*)item_p);
    }

    item_p = item_show_start_p;

    while(item_p)
    {
        if(as_ctrl_list_item_if_need_hide(list_p, item_p) == TRUE)
        {
            item_show_end_p = item_p;
            break;
        }
        else
        {
            item_p->base.set_hidden((as_control*)item_p, FALSE);
        }

        item_p = item_p->get_next_item((as_control*)item_p);
    }

    item_p = item_show_end_p;

    while(item_p)
    {
        item_p->base.set_hidden((as_control*)item_p, TRUE);
        item_p = item_p->get_next_item((as_control*)item_p);
    }
}

static as_ret as_ctrl_list_set_scrollbar_show(as_control* self, ASINT if_show)
{
    as_list* list_p = (as_list*)self;

    if(!list_p || list_p->if_show_scrollbar == if_show)
        return AS_INVALID_PARAM;

    list_p->if_show_scrollbar = if_show;
    return AS_OK;
}

static as_ret as_ctrl_list_set_scrollbar_img(as_control* self, as_rect area, as_image_src_type type,
        ASWSTR tail_path, ASWSTR tile_path)
{
    as_list* list_p = (as_list*)self;

    if(!list_p || !tail_path || !tile_path)
    {
        return AS_INVALID_PARAM;
    }

    if(list_p->scrollbar_tail_img)
    {
        as_ctrl_destroy((as_control*)list_p->scrollbar_tail_img);
    }

    if(list_p->scrollbar_tile_img)
    {
        as_ctrl_destroy((as_control*)list_p->scrollbar_tile_img);
    }

    list_p->scrollbar_tail_img = as_ctrl_image_create(area, type, tail_path, FALSE);

    if(!list_p->scrollbar_tail_img)
        return AS_MEMORY_NOT_ENOUGH;

    list_p->scrollbar_tile_img = as_ctrl_image_create(area, type, tile_path, FALSE);

    if(!list_p->scrollbar_tile_img)
    {
        as_ctrl_destroy((as_control*)list_p->scrollbar_tail_img);
        return AS_MEMORY_NOT_ENOUGH;
    }

    list_p->scrollbar_tile_img->set_show_type((as_control*)list_p->scrollbar_tile_img, AS_IMAGE_TILED);
    list_p->scrollbar_min_height = list_p->scrollbar_tail_img->img_h;
    list_p->scrollbar_width = list_p->scrollbar_tail_img->img_w;
    return AS_OK;
}

static as_ret as_ctrl_list_set_scrollbar_alpha(as_control* self, ASINT alpha)
{
    as_list* list_p = (as_list*)self;

    if(!list_p)
        return AS_INVALID_PARAM;

    if(alpha < 0)
        alpha = 0;

    if(alpha > 255)
        alpha = 255;

    if(list_p->scrollbar_tail_img)
        list_p->scrollbar_tail_img->set_alpha((as_control*)list_p->scrollbar_tail_img, alpha);

    if(list_p->scrollbar_tile_img)
        list_p->scrollbar_tile_img->set_alpha((as_control*)list_p->scrollbar_tile_img, alpha);

    return AS_OK;
}

static as_ret as_ctrl_list_set_scrollbar_color(as_control* self, ASUINT color, ASINT width)
{
    as_list* list_p = (as_list*)self;

    if(!list_p || list_p->scrollbar_color == color)
        return AS_INVALID_PARAM;

    list_p->scrollbar_color = color;
    list_p->scrollbar_width = width;
    return AS_OK;
}

//static void as_ctrl_list_set_pen_up_cb(as_control* self, as_list_pen_up_cb cb)
//{
//    as_list* list_p = (as_list*)self;
//
//    if(!list_p || !cb)
//    {
//        return;
//    }
//
//    list_p->penup_cb = cb;
//}

static void as_ctrl_list_move(as_control* ctrl_p, ASINT x_0ff, ASINT y_off)
{
    as_list* list_p = (as_list*)ctrl_p;
    as_listitem* item_p;

    if(!list_p)
        return;

    item_p = list_p->head;

    while (item_p)
    {
        item_p->base.move((as_control*)item_p, x_0ff, y_off);
        item_p = item_p->get_next_item((as_control*)item_p);
    }

    as_ctrl_list_update_items_hidden_state(list_p);
}


static as_ret as_ctrl_list_pen_hdlr(as_control* ctrl_p, ASINT event, ASINT x, ASINT y)
{
    as_list* list_p = (as_list*)ctrl_p;
    as_listitem* item_p;

    if(!list_p)
        return AS_INVALID_PARAM;

    if(list_p->head == NULL || ctrl_p->if_hidden)
        return AS_IGNORE;

    if(event == AS_EVT_TOUCH_DOWN)
    {
        if(list_p->is_taped == FALSE && AS_POINT_IN_RECT(x, y, list_p->base.area))
        {
            list_p->is_taped = TRUE;
            list_p->is_moved = FALSE;
            list_p->last_pos.x = list_p->tap_pos.x = (ASINT16)x;
            list_p->last_pos.y = list_p->tap_pos.y = (ASINT16)y;
            item_p = as_ctrl_list_get_item_from_point(list_p, x, y);

            if(item_p && item_p != list_p->select_item)
            {
                list_p->set_selected_idx((as_control*)list_p, list_p->get_idx_from_item((as_control*)list_p, item_p));

                if(list_p->last_select_item && !list_p->last_select_item->base.if_hidden)
                {
                    list_p->last_select_item->base.paint_hdlr((as_control*)list_p->last_select_item);
                }

                if(list_p->select_item && !list_p->select_item->base.if_hidden)
                {
                    list_p->select_item->base.paint_hdlr((as_control*)list_p->select_item);
                }

                if(list_p->if_show_scrollbar)
                {
                    as_ctrl_list_paint_scrollbar(list_p);
                }

                as_set_layer_clip_ex(0, &list_p->base.area);
                as_flush();
            }
            else if(item_p && item_p == list_p->select_item)
            {
                item_p->base.pen_hdlr((as_control*)item_p, event, x, y);
            }

            return AS_OK;
        }
        else
            return AS_IGNORE;
    }
    else if(event == AS_EVT_TOUCH_MOVE && list_p->is_taped == TRUE)
    {
        ASINT head_off = list_p->head->base.area.y - list_p->base.area.y;
        ASINT tail_off = (list_p->tail->base.area.y + list_p->tail->base.area.h) - (list_p->base.area.y + list_p->base.area.h);

        if(head_off >= 0 && (y - list_p->last_pos.y) > 0)
            return AS_OK;

        if(tail_off <= 0 && (y - list_p->last_pos.y) < 0)
            return AS_OK;

        list_p->is_moved = TRUE;
        //if(head_off >= list_p->base.area.h/2 || tail_off >= -list_p->base.area.h/2)
        {
            list_p->base.move((as_control*)list_p, 0, y - list_p->last_pos.y);
            //list_p->base.paint_hdlr((as_control*)list_p);
            as_ctrl_list_paint_items(list_p);
            as_set_layer_clip_ex(0, &list_p->base.area);
            as_ctrl_flush();
        }
        list_p->last_pos.x = (ASINT16)x;
        list_p->last_pos.y = (ASINT16)y;
        return AS_OK;
    }
    else if(event == AS_EVT_TOUCH_UP && list_p->is_taped == TRUE)
    {
        ASINT head_off = list_p->head->base.area.y - list_p->base.area.y;
        ASINT tail_off = (list_p->tail->base.area.y + list_p->tail->base.area.h) - (list_p->base.area.y + list_p->base.area.h);
        ASINT if_need_deal_cb = FALSE;

        if(head_off > 0 || (head_off < 0 && as_ctrl_list_if_items_need_scroll_up(list_p) == FALSE))
        {
            list_p->base.move((as_control*)list_p, 0, -head_off);
            as_ctrl_list_paint_items(list_p);
            as_ctrl_flush();
        }

        if(tail_off < 0 && as_ctrl_list_if_items_need_scroll_up(list_p) == TRUE)//and items are more than one page
        {
            list_p->base.move((as_control*)list_p, 0, -tail_off);
            as_ctrl_list_paint_items(list_p);
            as_ctrl_flush();
        }

        if(list_p->is_moved == FALSE && list_p->select_item &&
           list_p->select_item == as_ctrl_list_get_item_from_point(list_p, x, y))
        {
            if_need_deal_cb = TRUE;
        }

        list_p->is_taped = FALSE;
        list_p->is_moved = FALSE;

        if(if_need_deal_cb)
        {
            if(list_p->select_item)
            {
                list_p->select_item->base.pen_hdlr((as_control*)list_p->select_item, event, x, y);
                //list_p->select_item->pen_up_cb(list_p->select_item);
            }

            //if(list_p->penup_cb)
            //{
            //    list_p->penup_cb((as_control*)list_p);
            //}
        }

        return AS_OK;
    }
    else
    {
        return AS_IGNORE;
    }
}

static as_ret as_ctrl_list_key_hdlr(as_control* ctrl_p, ASINT event, ASINT keycode)
{
    as_list* list_p = (as_list*)ctrl_p;

    if(!list_p)
        return AS_INVALID_PARAM;

    if(ctrl_p->if_hidden)
        return AS_IGNORE;

    if(list_p->item_counts <= 0)
        return AS_IGNORE;

    if(keycode == AS_KEY_DOWN && event == AS_EVT_KEY_DOWN)
    {
        list_p->select_next_item((as_control*)list_p);
        list_p->adjust_select_item_position((as_control*)list_p);
        list_p->base.paint_hdlr((as_control*)list_p);
        as_ctrl_flush();
        return AS_OK;
    }
    else if(keycode == AS_KEY_UP && event == AS_EVT_KEY_DOWN)
    {
        list_p->select_prev_item((as_control*)list_p);
        list_p->adjust_select_item_position((as_control*)list_p);
        list_p->base.paint_hdlr((as_control*)list_p);
        as_ctrl_flush();
        return AS_OK;
    }
    else
    {
        if(list_p->select_item)
            return list_p->select_item->base.key_hdlr((as_control*)list_p->select_item, event, keycode);
        else
            return AS_IGNORE;
    }
}

static void as_ctrl_list_clean_items(as_control* self)
{
    as_listitem* tmp_p, *tmp_prev_p;
    as_list* list_p = (as_list*)self;

    if(!list_p)
        return;

    tmp_p = list_p->tail;

    while(tmp_p)
    {
        tmp_prev_p = tmp_p->get_prev_item((as_control*)tmp_p);
        as_ctrl_listitem_destroy(tmp_p);
        tmp_p = tmp_prev_p;
    }

    list_p->last_select_item = list_p->select_item = list_p->head = list_p->tail = NULL;
    list_p->select_idx = -1;
    list_p->last_select_idx = -1;
    list_p->item_counts = 0;
    list_p->items_total_height = 0;
}

static as_ret as_ctrl_list_set_empty_text(as_control* self, ASWSTR empty_str, ASUINT color)
{
    as_list* list_p = (as_list*)self;
    ASINT len, len1;

    if(!list_p || !empty_str)
        return AS_INVALID_PARAM;

    len = as_wstrlen(empty_str);

    if(len <= 0)
        return AS_INVALID_PARAM;

    len1 = AS_MIN(len, EMPTY_TEXT_LEN);
    as_wstrncpy(list_p->empty_text, empty_str, len1);
    list_p->empty_text_color = color;
    return AS_OK;
}

static void as_ctrl_list_set_hidden(as_control* ctrl_p, ASINT if_hidden)
{
    as_list* list_p = (as_list*)ctrl_p;
    ASINT i;

    if(!list_p)
        return;

    if(ctrl_p->if_hidden != if_hidden)
    {
        ctrl_p->if_hidden = if_hidden;

        for(i = 0; i < list_p->item_counts; i++)
        {
            as_listitem* li_p = list_p->get_item_from_idx((as_control*)list_p, i);

            if(li_p)
            {
                li_p->base.set_hidden((as_control*)li_p, if_hidden);
            }
        }
    }
}

static as_ret as_ctrl_list_set_grid_line(as_control* self, ASINT line_cnts, ASUINT* line_colors)
{
    as_list* list_p = (as_list*)self;
    ASINT i;

    if(!list_p || list_p->grid_color_cnts == line_cnts)
        return AS_INVALID_PARAM;

    list_p->grid_color_cnts = line_cnts;

    if(line_colors)
    {
        for(i = 0; i < GRID_LINE_MAX; i++)
        {
            list_p->grid_colors[i] = *(line_colors + i);
        }
    }

    return AS_OK;
}

as_list* as_ctrl_list_create(as_rect area)
{
    as_list* list_p = NULL;
    list_p = as_calloc(sizeof(as_list));

    if(!list_p)
        return NULL;

    as_ctrl_init((as_control*)list_p, area);
    list_p->base.paint_hdlr = as_ctrl_list_paint;
    list_p->base.ctrl_type = AS_CTRL_LIST;
    list_p->base.key_hdlr = as_ctrl_list_key_hdlr;
    list_p->base.pen_hdlr = as_ctrl_list_pen_hdlr;
    list_p->base.move = as_ctrl_list_move;
    list_p->base.set_hidden = as_ctrl_list_set_hidden;
    list_p->add_item = as_ctrl_list_add_item;
    list_p->delete_item = as_ctrl_list_delete_item;
    list_p->head = list_p->tail = NULL;
    list_p->get_node_counts = as_ctrl_list_get_node_counts;
    list_p->set_bg_color = as_ctrl_list_set_bg_color;
    list_p->set_bg_image = as_ctrl_list_set_bg_image;
    list_p->set_selected_idx = as_ctrl_list_set_selected_idx;
    list_p->select_idx = -1;
    list_p->last_select_idx = -1;
    list_p->select_item = NULL;
    list_p->last_select_item = NULL;
    list_p->items_total_height = 0;
    list_p->select_prev_item = as_ctrl_list_select_prev_item;
    list_p->select_next_item = as_ctrl_list_select_next_item;
    list_p->adjust_select_item_position = as_ctrl_list_adjust_selected_item_position;
    list_p->get_idx_from_item = as_ctrl_list_get_idx_from_item;
    list_p->get_item_from_idx = as_ctrl_list_get_item_from_idx;
    //list_p->set_pen_up_cb = as_ctrl_list_set_pen_up_cb;
    list_p->set_scrollbar_show = as_ctrl_list_set_scrollbar_show;
    list_p->set_scrollbar_img = as_ctrl_list_set_scrollbar_img;
    list_p->set_scrollbar_alpha = as_ctrl_list_set_scrollbar_alpha;
    list_p->set_scrollbar_color = as_ctrl_list_set_scrollbar_color;
    list_p->clean_items = as_ctrl_list_clean_items;
    list_p->set_empty_text = as_ctrl_list_set_empty_text;
    list_p->set_grid_line = as_ctrl_list_set_grid_line;
    as_wstrcpy(list_p->empty_text, as_ucs2_from_gb2312(AS_LIST_EMPTY_TEXT));
    list_p->scrollbar_width = 10;
    list_p->scrollbar_min_height = 8;
    return list_p;
}

as_ret as_ctrl_list_destroy(as_list* ctrl_list_p)
{
    if(!ctrl_list_p)
        return AS_INVALID_PARAM;

    ctrl_list_p->clean_items((as_control*)ctrl_list_p);

    if(ctrl_list_p->bg_image)
    {
        as_ctrl_destroy((as_control*)ctrl_list_p->bg_image);
    }

    if(ctrl_list_p->scrollbar_tail_img)
    {
        as_ctrl_destroy((as_control*)ctrl_list_p->scrollbar_tail_img);
    }

    if(ctrl_list_p->scrollbar_tile_img)
    {
        as_ctrl_destroy((as_control*)ctrl_list_p->scrollbar_tile_img);
    }

    as_free(ctrl_list_p);
    ctrl_list_p = NULL;
    return AS_OK;
}